package com.basker.pisces.core.serialize.xml;

import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Collection;

import org.dom4j.Branch;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.XMLWriter;
import org.springframework.util.CollectionUtils;

import com.basker.pisces.core.ISupportSort;
import com.basker.pisces.core.meta.ICollectionField;
import com.basker.pisces.core.meta.IDataField;
import com.basker.pisces.core.meta.IDataObjectField;
import com.basker.pisces.core.meta.IDataObjectMeta;
import com.basker.pisces.core.meta.IField;
import com.basker.pisces.core.meta.IReferenceField;
import com.basker.pisces.core.utils.ConvertUtils;
import com.basker.pisces.exception.ExceptionFactory;

/**
 * XmlWriter,负责将对象序列化成xml
 *
 * @author hangwen
 */
class DataContractXmlWriter {
    private Writer writer;
    private boolean isPrettyFormat = false;
    private IDataContractXmlSerializeHandler serializerHandler;

    public DataContractXmlWriter(Writer writer) {
        this(writer, false);
    }

    public DataContractXmlWriter(Writer writer, boolean isPrettyFormat) {
        this.writer = writer;
        this.isPrettyFormat = isPrettyFormat;
    }

    public void setSerializeHandler(IDataContractXmlSerializeHandler serializerHandler) {
        this.serializerHandler = serializerHandler;
    }

    /**
     * 对象信息转化为dom对象写入 writer
     *
     * @param dataObject
     * @throws IOException
     */
    public void writeDataContract(Object dataObject) {
        if (dataObject == null) {
            return;
        }

        Document xmlDoc = DocumentHelper.createDocument();
        writeDataContract0(xmlDoc, dataObject);

        if (!xmlDoc.hasContent()) {
            return;
        }

        XMLWriter xmlWriter = createXmlWriter(this.writer, this.isPrettyFormat);
        try {
            xmlWriter.write(xmlDoc);
        } catch (IOException e) {
            throw ExceptionFactory.createRuntimeException(e);
        }
    }

    private XMLWriter createXmlWriter(Writer writer, boolean isPrettyFormat) {
        if (isPrettyFormat) {
            OutputFormat format = OutputFormat.createPrettyPrint();
            format.setEncoding("utf-8");
            format.setIndent(true);
            format.setIndent("\t");
            format.setNewlines(true);

            return new XMLWriter(writer, format);
        } else {
            return new XMLWriter(writer);
        }
    }

    /**
     * 对象信息转化为dom对象写入 writer
     *
     * @param content
     */
    private String formatXml(String content) {
        Writer writer = new StringWriter();
        XMLWriter xmlWriter = this.createXmlWriter(writer, this.isPrettyFormat);

        try {
            Document xmlDoc = DocumentHelper.parseText(content);
            xmlWriter.write(xmlDoc);
            return writer.toString();
        } catch (Exception e) {
            throw ExceptionFactory.createRuntimeException(e);
        }

    }

    private void setSortValue(Collection<?> coll) {
        int index = 0;

        if (CollectionUtils.isEmpty(coll)) {
            return;
        }

        for (Object itemObj : coll) {
            if (itemObj instanceof ISupportSort) {
                ((ISupportSort) itemObj).setSort(index);
            }

            index++;
        }
    }

    /**
     * 在 parentXmlElement上附加一个元素，该元素的内容是dataObject
     *
     * @param parentXmlElement
     * @param dataObject
     */
    private void writeDataContract0(Branch parentXmlElement, Object dataObject) {
        if (dataObject == null) {
            return;
        }

        IDataObjectMeta dataObjectMeta = this.serializerHandler.getDataObjectMeta(dataObject);
        String elementName = this.serializerHandler.getDataObjectMetaElementName(dataObjectMeta);
        Element objectXmlElement = parentXmlElement.addElement(elementName);

        if (this.serializerHandler.writeClassName()) {
            objectXmlElement.addAttribute(IDataContractXmlSerializeHandler.TYPE_ATTR_NAME,
                    dataObject.getClass().getName());
        }

        for (IField field : dataObjectMeta.getFields()) {
            String fieldName = field.getName();
            Object val = field.getValue(dataObject);

            if (field instanceof IDataField) {
                if (val != null) {
                    String textVal = ConvertUtils.convert(val, String.class);
                    if (textVal == null) {
                        continue;
                    }

                    Element fieldXmlElement = objectXmlElement.addElement(fieldName);

                    if (((IDataField) field).isCData()) {
                        String cdata = formatXml(textVal);
                        fieldXmlElement.addCDATA(cdata);
                    } else {
                        fieldXmlElement.addText(textVal);
                    }
                }
            } else if (field instanceof IDataObjectField) {
                boolean writeField = true;
                if (field instanceof IReferenceField) {
                    writeField = false;
                }

                if (writeField && val != null) {
                    Element fieldXmlElement = objectXmlElement.addElement(fieldName);
                    writeDataContract0(fieldXmlElement, val);
                }
            } else if (field instanceof ICollectionField) {
                Element fieldXmlElement = objectXmlElement.addElement(fieldName);

                Collection<?> coll = (Collection<?>) val;

                if (coll != null) {
                    this.setSortValue(coll);

                    for (Object itemObj : coll) {
                        writeDataContract0(fieldXmlElement, itemObj);
                    }
                }

                if (!fieldXmlElement.hasContent()) {
                    objectXmlElement.remove(fieldXmlElement);
                }
            }
        }
    }

}
